home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1998 / MacHack 1998.toast / The Hacks! / Talking KeyBoard / Source / filter.c < prev    next >
Text File  |  1998-06-06  |  11KB  |  290 lines

  1. // Program Author: Paul Baxter
  2. //    pbaxter@assistivetech.com
  3. //
  4. //
  5.  
  6. #include <Ctype.h>
  7. #include <DeskBus.h>
  8. #include <Speech.h>
  9. #include <Retrace.h>
  10.  
  11. #include "filter.h"
  12. #include "speech.h"
  13. #include "pref.h"
  14. #include "globals.h"
  15.  
  16.  
  17. // ***********************************************************************************
  18. // ***********************************************************************************
  19.  
  20. /*
  21.     The Not-So-Simple FAT jGNEFilter 
  22.         Original Code by Matt Slot (fprefect@umich.edu), 6/2/95 
  23.             with help and criticism (lots!) from Ed Wynne (arwyn@umich.edu).
  24.             modified by Paul Baxter (pbaxter@assistivetech.com).4/17/97
  25.     
  26.     Quick Intro
  27.         Since jGNEFilters are nasty things in 68k, and pretty much impossible 
  28.         in PPC, writing simple and cross-compiling handler code is also. To 
  29.         facilitate use of this, I have written some interface routines to hide
  30.         some of the complexity from the application programmer.
  31.         
  32.     /    Ptr InstallEventFilter(FilterHelperUPP helperProc, Ptr helperData);
  33.     |        Installs a jGNEFilter which can properly call a callback from within
  34.     |        the application. The callback handles incoming events wither either 
  35.     |        68k or PPC code, and gets passed the data pointer you send from here.
  36.     |        Install() returns a pointer the jGNEFilter installed in the system heap
  37.     |        or NIL to indicate an error.
  38.  
  39.     /    Ptr ReleaseEventFilter(Ptr filterProc);
  40.     |        Pass in the pointer to the jGNEFilter, and this routine will disable 
  41.     |        the handling and try to dispose of its storage in the System Heap.
  42.     |        You *must* do this if your helper function is going to disappear when
  43.     |        the application closes. 
  44.     
  45.     /    asm void EventFilter(void);
  46.     |        This is *not* the installed jGNEFilter... well not really. There is no way
  47.     |        to get the same code to compile on both 68K and PPC, so this routine is
  48.     |        just a demo function. The real code has been disassembled from the 68k
  49.     |        source and placed into a Hex String suitable for StuffHex()'ing into the
  50.     |        System Heap on both 68K and PPC machines. Note that the code simply calls 
  51.     |        the ProcPtr for the Helper function blindly... whether 68k or PPC. 
  52.     |        WARNING: Changing the EventFilter() will do nothing! -- to apply changes, 
  53.     |        you need to take the modified 68K machine code and refresh the declared
  54.     |        kGNEFilterHexData string constant.
  55.  
  56.     /    void EventFilterHelper(EventRecord *theEvent, Ptr helperData);
  57.     |        This callback is the workhorse of the event filter. Once installed, this  
  58.     |        routine sees every event that gets harvested and has an opportunity to
  59.     |        modify the record before the front application gets to see it. Ideally
  60.     |        this function can do the necessary work itself or pass off the event info
  61.     |        to the home application.
  62.     |        WARNING: For 68k code, the routine will be called from the current app's
  63.     |        context (A5/Globals, Rsrc File, HeapZone). PPC code will have a valid RTOC
  64.     |        (Globals access) but not Rsrc File, HeapZone, etc.
  65.     
  66.  
  67.     How this all works:
  68.         
  69.         Since I didn't want to write the jGNEFilter in C (OK, I couldn't figure out
  70.         how to), the code is installed as raw 68k by StuffHex()'ing a precompiled
  71.         routine -- identical to the one as declared below -- into the System Heap.
  72.         
  73.         The jGNEFilter keeps 3 pieces of data inline: the next filter in the chain,
  74.         a pointer to the helper routine, and some extra data to pass to the helper.
  75.         Most importantly, we may not be able to remove the filter from the calling
  76.         chain...  the architecture just doesn't permit it! If we are able to safely
  77.         pull the filter out, we do. Otherwise the next best solution is to keep a flag,
  78.         that we can clear when we want to disable the functionality -- in fact, we
  79.         set or clear the pointer to the Helper Proc as the flag.
  80.         
  81.         Finally, the helper function is the meat of our jGNEFilter; it does the work
  82.         of the active filter. In the case of a 68K helper, it is accessed via a 
  83.         simple ProcPtr. In the case of a PPC helper, the installer sets up a valid
  84.         RoutineDescriptor (in the System Heap with the jGNEFilter) to invoke a 
  85.         MixedMode switch between the 68K caller (filter) and PPC routine (helper).
  86.         Again, when releasing the filter the handler disposes the descriptor and 
  87.         clear the inline ProcPtr/flag, since the helper function will probably be
  88.         disappearing when the application quits.
  89.         
  90.         If you are picking out events to handle within your app, my suggestion is to
  91.         keep a secondary Queue of events in the System Heap -- remember, you must 
  92.         allocate new EventRecords (since the current event belongs to the calling
  93.         app) into the System Heap (you need a heap that both the current process and
  94.         your own process can access). Given these events, your main event loop can 
  95.         suck out clicks or keydowns for dispatching internally and safely within your
  96.         own context.
  97.         
  98.         Also, Text Service windows don't receive Activate or Update events... you 
  99.         must check for those manually within your own event loop and handle them.
  100.     
  101. */
  102.  
  103. // ***********************************************************************************
  104. // * InstallEventFilter
  105. // *                install our event filter
  106. // ***********************************************************************************
  107. Ptr InstallEventFilter(FilterHelperUPP helperProc, Ptr helperData)
  108. {
  109.     Ptr filterProc, data;
  110.     
  111.     // Create a duplicate function in the System Heap (so its *alway* there) and
  112.     //   copy the data across. Note: even though it is technically a function, we
  113.     //   can still treat it as data safely until it has been installed and called.
  114.     filterProc = NewPtrSys(sizeof(kGNEFilterHexData)/2 + 1);
  115.     if (! filterProc) return(0);
  116.     StuffHex(filterProc, kGNEFilterHexData);
  117.     
  118.     // Get and install the current filter as the next filter in the chain.
  119.     data = (Ptr) LMGetGNEFilter();
  120.     BlockMove(&data, filterProc + kNextFilterOffset, sizeof(data));
  121.  
  122.     // Get and install the Helper function to do the real work (and as a flag to
  123.     //   indicate we are in business and accepting events). Remember that if we
  124.     //   generating PPC code, it is necessary to establish a Routine Descriptor.
  125.     SetZone(SystemZone());
  126.     data = (Ptr) NewFilterHelperProc(helperProc);
  127.     if (! data) return(0);
  128.     BlockMove(&data, filterProc + kEventHelperOffset, sizeof(data));
  129.     SetZone((Zone*)ApplicationZone());
  130.  
  131.     // If the caller wants to pass data to the jGNEFilter Helper function. This
  132.     //   pointer (or handle if desired) *must* be allocated in the System Heap
  133.     //   if you don't plan on releasing the Filter before quitting. If you plan
  134.     //   on releasing the filter, then either the App or Sys heap will suffice.
  135.     data = helperData;
  136.     BlockMove(&helperData, filterProc + kEventHelperDataOffset, sizeof(data));
  137.     
  138.     // Install us, we are ready to do some work!
  139.     LMSetGNEFilter((GNEFilterUPP) filterProc);
  140.     
  141.     return(filterProc);
  142. }
  143.  
  144. // ***********************************************************************************
  145. // * ReleaseEventFilter
  146. // *                        remove our event filter
  147. // ***********************************************************************************
  148. Ptr ReleaseEventFilter(Ptr filterProc)
  149. {
  150.     Ptr data;
  151.  
  152.     if (! filterProc)
  153.         return(0);
  154.  
  155.     // Clear the Helper location as an indicator that we have closed up shop. The
  156.     //   filter itself may lingers in the System Heap until shutdown unless we can
  157.     //   find a way to extract it from the chain (see below). On the other hand, the 
  158.     //   filter has been written so that if the Helper function pointer is NIL, the 
  159.     //   filter will do nothing at all. Let's zero it out for that (hopeful) case.
  160.     BlockMove(filterProc + kEventHelperOffset, &data, sizeof(data));
  161.     if (data)
  162.         DisposeRoutineDescriptor((UniversalProcPtr) data);
  163.     data = 0;
  164.     BlockMove(&data, filterProc + kEventHelperOffset, sizeof(data));
  165.     
  166.     // If the installed filterProc is the first one in the chain, then we should
  167.     //    be able remove it and replace it with the next one (the one we would
  168.     //    normally jump to). If we can dispose the filterProc buffer, then we can
  169.     //    can recover those 50 bytes that remain in the System Heap.
  170.     // Thanks to HoverBar's Guy Fullerton (hedgeboy@realm.net) for the suggestion.
  171.     if (filterProc == (Ptr) LMGetGNEFilter()) {
  172.         // Remove our filterProc from the chain.
  173.         BlockMove(filterProc + kNextFilterOffset, &data, sizeof(data));
  174.         LMSetGNEFilter((GNEFilterUPP) data);
  175.         
  176.         BlockMove(filterProc + kEventHelperDataOffset, &data, sizeof(data));
  177.         DisposePtr(filterProc);
  178.     }
  179.      else {
  180.         // Grab the data that was passed when initialized or as set in the Helper
  181.         //   function. The caller can then deallocate it if desired or necessary.    
  182.         BlockMove(filterProc + kEventHelperDataOffset, &data, sizeof(data));
  183.     }
  184.     
  185.     return(data);
  186. }
  187.  
  188. // ***********************************************************************************
  189. // *    EventFilter
  190. //                                Eventfilter 68K code only
  191. // ***********************************************************************************
  192. #if GENERATING68K
  193. asm void EventFilter()
  194. {
  195.     bra.s    Continue
  196.         
  197. Next_Filter:
  198.     dc.l    0                        // Saved Address of Next jGNEFilter in the chain.
  199.                                     //   We jump directly to it, no JSR's or RTS's.
  200. Event_Helper:
  201.     dc.l    0                        // Pointer to Helper function in our application.
  202.                                     //   We clear it to NIL when we quit as a flag
  203. Event_Helper_Data:
  204.     dc.l    0                        // Promised storage for the Helper function, 
  205.                                     //   which can modify the pointer dynamically.
  206. Continue:
  207.  
  208.     fralloc
  209.  
  210.     // Save the Volatile registers for safety
  211.     movem.l    d0-d2/a0-a2, -(a7)            
  212.  
  213.     // Load the Helper from Inline Storage and test it. If Helper is NIL,
  214.     //   then our handler was released -- and we just jump to the next Filter
  215.     move.l    Event_Helper, a0
  216.     move.l    a0, d0
  217.     tst.l    d0                            
  218.     beq        End_Filter
  219.     
  220.     // Straight C Calling Conventions, call the Helper function
  221.     move.l    Event_Helper_Data, -(a7)
  222.     move.l    a1, -(a7)
  223.     move.l    Event_Helper, a0
  224.     jsr        (a0)
  225.     add.l    #8, a7
  226.         
  227. End_Filter:
  228.  
  229.     // Clean up the same way we got here
  230.     movem.l    (a7)+, d0-d2/a0-a2
  231.  
  232.     frfree
  233.     
  234.     // Jump to the next filter in the chain
  235.     move.l    Next_Filter, a0
  236.     jmp        (a0)
  237. }
  238. void EndEventFilter() { }
  239. #endif GENERATING68K
  240.  
  241.  
  242. // ***********************************************************************************
  243. // *    EventFilterHelper
  244. // *                            heart of our filter
  245. // ***********************************************************************************
  246. void EventFilterHelper(EventRecord *theEvent, Ptr helperData)
  247. {
  248.     long saveA5;
  249.     EvQEl *fwdEvent;
  250.     Boolean fwdThisEvent = false;
  251.  
  252.     // This only does something in 68K code. We now have access to globals,
  253.     //   which PPC get for free from CFM; however, we won't have access to 
  254.     //   our application's Resource file/chain or HeapZone. Be careful!
  255.     saveA5 = SetA5((long) helperData);
  256.  
  257.     switch(theEvent->what) {
  258.         case keyDown:
  259.             // we will send this to the application
  260.             fwdThisEvent = true;
  261.             break;
  262.  
  263.         case mouseDown:
  264.             // we will send this to the application
  265.             fwdThisEvent = true;
  266.             break;
  267.  
  268.         default:
  269.             break;
  270.     }
  271.     if (fwdThisEvent) {
  272.         // We have discovered that this event deserves full attention. Forward it to
  273.         //   our home application by Q'ing it up as a pointer in the System Heap
  274.         fwdEvent = (EvQEl *) NewPtrSys(sizeof(*fwdEvent));
  275.         fwdEvent->qLink = 0;
  276.         fwdEvent->qType = evType;
  277.         BlockMove(&theEvent->what, &fwdEvent->evtQWhat, sizeof(EventRecord));
  278.  
  279.         Enqueue((QElem *) fwdEvent, &gForwardedEvents);
  280.         
  281.         // Let our app get the CPU to handle the event quickly
  282.         WakeUpProcess(&gPSN);
  283.     }
  284.  
  285.     // Restore the (68K) context before leaving
  286.     SetA5(saveA5);
  287. }
  288.  
  289.  
  290.